----------------------------------------------------------------------------------
-- Company: 			StrathSat-R	
-- Engineer:			Thomas Parry
-- 
-- Create Date:    	12:00:20 09/02/2012 
-- Design Name: 		SD card host main state machine
-- Module Name:    	host - behavioural
-- Project Name: 		FPGA data storage
-- Target Devices: 	Spartan 6
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity host is
	generic
	(
		CMD_LENGTH			:	integer := 6;
		ARGUMENT_LENGTH	:	integer := 32;
		CMD_CRC_LENGTH		:	integer := 7;
		DATA_LENGTH			:	integer := 4096;
		DATA_INPUT_WIDTH	:	integer := 8;
		DATA_CRC_LENGTH	:	integer := 16
	);
   port 
	( 
		Clk 					: 	in  		std_logic;
		Reset					:	in			std_logic;
      Data 					: 	in  		std_logic_vector(7 downto 0);
      Sd_Cmd 				: 	inout		std_logic;
      Sd_Data 				: 	inout		std_logic;				-- TODO change to 4 bit bus
		Sd_Clk				:	out		std_logic;
		Start					:	in			std_logic;
		New_State			:	out		std_logic;
		Cmd_Prev_Rx			: 	out		std_logic_vector(47 downto 0);
		Cmd_Prev_Tx			: 	out		std_logic_vector(CMD_LENGTH-1 downto 0);
		Resp_Found			:	out		std_logic;
		Proceed				:	in			std_logic;
		State_Prog			:	in			std_logic;
		Received				:	out		std_logic;
		Sent_Cmd				:	out		std_logic;
		Rec_Resp				:	out		std_logic;
		Data_Rec				: 	out		std_logic;
		Data_Sent			: 	out		std_logic;
		Data_Read_Out		:	out		std_logic_vector(255 downto 0);
		Data_Ready			:	out		std_logic;
		Rca_Out				:	out		std_logic_vector(15 downto 0)
	);
end host;


architecture behavioral of host is

---- functions

-- convert integer to std logic vector
function to_std_logic_vector (int : integer; width : integer) return std_logic_vector is
begin
	return std_logic_vector(to_unsigned(int,width));
end to_std_logic_vector;


---- components

-- command transmitter
component cmd_tx is
	generic
	(
		CMD_LENGTH			:	integer := 6;
		ARGUMENT_LENGTH	:	integer := 32;
		CRC_LENGTH			:	integer := 7
	);
   port
	( 
		Clk 					: in  std_logic;
      Strobe 				: in  std_logic;
      Cmd 					: in  std_logic_vector(CMD_LENGTH-1 downto 0);
      Argument 			: in  std_logic_vector(ARGUMENT_LENGTH-1 downto 0);
      Output 				: out std_logic;
      Done 					: out std_logic
	);
end component cmd_tx;

-- data transmitter
component data_tx is
	generic
	(
		DATA_LENGTH			:	integer := 4096;
		INPUT_WIDTH			:	integer := 8;
		CRC_LENGTH			:	integer := 16
	);
   port 
	( 
		Clk 					: in  std_logic;
      Strobe 				: in  std_logic;
      Data 					: in  std_logic_vector(DATA_INPUT_WIDTH-1 downto 0);
      Output 				: out std_logic;
      Done 					: out std_logic;
		Data_Req				: out std_logic
	);
end component data_tx;


---- constants



---- signals

-- command transmitter signals
signal cmd_strobe			:		std_logic;
signal cmd_command		:		std_logic_vector(CMD_LENGTH-1 downto 0);
signal cmd_argument		:		std_logic_vector(ARGUMENT_LENGTH-1 downto 0);
signal cmd_data			:		std_logic;
signal cmd_input			:		std_logic;
signal cmd_output			:		std_logic;
signal cmd_done			:		std_logic;
signal cmd_inout			:		std_logic;	-- defines direction of pin 0=in, 1=out
signal cmd_await			:		std_logic;

-- data transmitter signals 
signal data_strobe		:		std_logic;
signal data_data			:		std_logic_vector(DATA_INPUT_WIDTH-1 downto 0) := X"30";
signal data_input			:		std_logic;
signal data_output		:		std_logic;
signal data_done			:		std_logic;
signal data_req			:		std_logic;
signal data_inout			:		std_logic;	-- defines direction of pin 0=in, 1=out

-- response signals
signal resp_start			:		std_logic;
signal resp_stop			:		std_logic;
signal resp_await			:		std_logic;	
signal resp_count			:		integer range 0 to 47;
signal resp_content		:		std_logic_vector(47 downto 0) := (others => '0');
signal resp_prev			:		std_logic_vector(47 downto 0) := (others => '0');

-- clock signals
signal clk_slow			:		std_logic := '0';
signal clk_slow_en		:		std_logic;
signal clk_sd				:		std_logic := '0';

-- control signals
signal strobe_out			:		std_logic;

-- delay state signals
signal delay_incr			:		std_logic;
signal delay_count		:		integer range 0 to 127;
signal delay_complete	:		std_logic;

-- sd card parameters
signal rca					:		std_logic_vector(15 downto 0) := (others => '0');
signal low_wait			:		std_logic := '0';
signal data_found			:		std_logic := '0';

-- address for write and reads
signal address				:		std_logic_vector(31 downto 0) := X"00000100";
signal data_read			:		std_logic;
signal data_finish		:		std_logic;
signal data_r				:		std_logic_vector(255 downto 0) := (others => '1');


-- state machine signals
type host_state_type is
(
	idle,
	command0,
	command8,
	command55,
	command41,
	response1,
	command2,
	command3,
	command9,
	command7,
	command6,
	command25,
	delay,
	resp_wait,
	data_start,
	data_wait,
	command13,
	command12,
	disc,
	wait_read
);
signal host_state		:	host_state_type;
signal prev_state		:	host_state_type;
signal next_state		:	host_state_type;
signal app_state		:	host_state_type := command41;

begin

Rca_Out <= rca;

--------------------------------------------------------------------------------------------
-- main state machine

-- clocked process
SM_CLK : process(clk_sd) is
begin

	if rising_edge(clk_sd) then
	
		if Reset = '1' then
			host_state <= idle;
		else
	
			case host_state is
				----------------------------------------------------------------------
				when idle =>		if State_Prog = '1' then
											host_state <= command0;
										else
											host_state <= idle;
										end if;
										prev_state <= idle;
										clk_slow_en		<=		'1';
									
				----------------------------------------------------------------------
				when command0 => 	if cmd_done = '1' then
											host_state <= delay;
										else
											host_state <= command0;
										end if;
										Cmd_Prev_Tx	<=	to_std_logic_vector(0,CMD_LENGTH);
										next_state	<= command8;
										prev_state	<= command0;
										
				----------------------------------------------------------------------
				when command8 => 	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command8;
										end if;
										Cmd_Prev_Tx	<=	to_std_logic_vector(8,CMD_LENGTH);
										next_state	<= command55;
										prev_state	<= command8;
										
				----------------------------------------------------------------------
				when command55 => if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command55;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(55,CMD_LENGTH);
										next_state	<=	app_state;
										
				----------------------------------------------------------------------
				when command41 => if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command41;
										end if;
										Cmd_Prev_Tx	<=	to_std_logic_vector(41,CMD_LENGTH);
										next_state	<= response1;
										--next_state	<= command7;
										prev_state	<= command41;
										app_state 	<=	command41;
										
				----------------------------------------------------------------------
				when response1 => if resp_prev(39) = '1' then
											host_state <= command2;
										else
											host_state <= command55;
										end if;
										prev_state	<= response1;
										
				----------------------------------------------------------------------
				when command2 => 	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command2;
										end if;
										Cmd_Prev_Tx	<=	to_std_logic_vector(2,CMD_LENGTH);
										next_state	<= command3;
										prev_state	<= command2;
										
				----------------------------------------------------------------------
				when command3 => 	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command3;
										end if;		
										Cmd_Prev_Tx	<=	to_std_logic_vector(3,CMD_LENGTH);
										next_state	<= command7;
										prev_state	<= command3;
										
				----------------------------------------------------------------------
				when command9 => 	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command9;
										end if;		
										Cmd_Prev_Tx	<=	to_std_logic_vector(9,CMD_LENGTH);
										next_state	<= command7;
										prev_state	<= command9;
														
				----------------------------------------------------------------------
				when command7 =>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command7;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(7,CMD_LENGTH);
										next_state	<= command25;
										prev_state	<= command7;
										app_state 	<=	command6;
										--clk_slow_en	<=	'0';
										
				----------------------------------------------------------------------
				when command6 =>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command6;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(6,CMD_LENGTH);
										next_state	<= command25;
										prev_state	<= command6;
										
				----------------------------------------------------------------------
				when command25	=>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command25;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(25,CMD_LENGTH);
										next_state	<=	data_start;
										prev_state	<= command25;
 				
				----------------------------------------------------------------------
				when delay 		=>	if delay_complete = '1' and State_Prog = '1' then
											host_state <= command8;
										end if;
										
				----------------------------------------------------------------------
				when resp_wait	=>	if State_Prog = '1' then
										--if resp_stop = '1' and State_Prog = '1' then
										--if resp_stop = '1' or State_Prog = '1' then
											host_state <= next_state;
										end if;
										
				----------------------------------------------------------------------
				when data_start	=> if data_done = '1' then
												host_state <= data_wait;
											else	
												host_state <= data_start;
											end if;
											
				----------------------------------------------------------------------
				when data_wait		=> if data_found = '1' then
												host_state <= command13;
											else
												host_state <= data_wait;
											end if;
											
				----------------------------------------------------------------------
				when command13	=>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command13;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(13,CMD_LENGTH);
										next_state	<=	command13;
										prev_state	<= command13;
										
				----------------------------------------------------------------------
				when command12	=>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= command12;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(12,CMD_LENGTH);
										next_state	<= command13;
										prev_state	<= command12;
										
				----------------------------------------------------------------------
				when disc		=>	if cmd_done = '1' then
											host_state <= resp_wait;
										else
											host_state <= disc;
										end if;
										Cmd_Prev_Tx <= to_std_logic_vector(15,CMD_LENGTH);
										next_state	<=	disc;
										prev_state	<= disc;
				----------------------------------------------------------------------
				when wait_read =>	if data_finish = '1' then
											host_state <= command13;
										else
											host_state <= wait_read;
										end if;
										next_state	<=	command13;
										prev_state	<= wait_read;
				
				----------------------------------------------------------------------
				when others =>		host_state <= idle;
				
				----------------------------------------------------------------------							
			end case;
		end if;		
	end if;
end process SM_CLK;


SM_CMB : process(host_state) is
begin

	-- default signals
	cmd_command		<=		(others => '0');
	cmd_argument	<=		(others => '0');
	strobe_out		<=		'0';
	cmd_inout		<=		'0';
	data_inout		<=		'0';
	--clk_slow_en		<=		'0';
	resp_await		<=		'0';
	delay_incr		<=		'0';
	data_strobe		<=		'0';
	low_wait			<=		'0';
	data_read		<=		'0';

	case host_state is
		------------------------------------------------------------------------------
		when idle			=>
		------------------------------------------------------------------------------
		when command0		=>	cmd_command		<=	to_std_logic_vector(0,CMD_LENGTH);
									cmd_argument 	<= (others => '0');	-- reserved
									strobe_out 		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when command8		=>	cmd_command		<=	to_std_logic_vector(8,CMD_LENGTH);
									cmd_argument	<=	X"00000" & X"1" & X"AA";	-- reserved & voltage supplied (2.7-3.6V) & check pattern
									strobe_out 		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when command55 	=> cmd_command		<=	to_std_logic_vector(55,CMD_LENGTH);
									cmd_argument	<= rca & X"0000"; -- RCA (0x0000) & stuff bits
									strobe_out		<= '1';
									cmd_inout		<=	'1';
		------------------------------------------------------------------------------
		when command41		=> cmd_command		<=	to_std_logic_vector(41,CMD_LENGTH);
									cmd_argument 	<=	"0" & "1" & "0" & "1" & "000" & "0" & "11111111" & X"0000";
									strobe_out 		<= '1';
									cmd_inout		<=	'1';
		------------------------------------------------------------------------------
		when response1 	=>	
		------------------------------------------------------------------------------
		when command2		=>	cmd_command		<=	to_std_logic_vector(2,CMD_LENGTH);
									cmd_argument	<= (others => '0');	-- reserved
									strobe_out 		<= '1';
									cmd_inout		<=	'1';
		------------------------------------------------------------------------------
		when command3		=>	cmd_command		<=	to_std_logic_vector(3,CMD_LENGTH);
									cmd_argument	<=	(others => '0');	-- reserved
									strobe_out 		<= '1';
									cmd_inout		<=	'1';
		------------------------------------------------------------------------------
		when command9		=>	cmd_command		<=	to_std_logic_vector(9,CMD_LENGTH);
									cmd_argument	<=	rca & X"0000";
									strobe_out 		<= '1';
									cmd_inout		<=	'1';
		------------------------------------------------------------------------------
		when command7 		=> cmd_command		<= to_std_logic_vector(7,CMD_LENGTH);
									cmd_argument	<= rca & X"0000";
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when command6 		=>	cmd_command		<= to_std_logic_vector(6,CMD_LENGTH);
									cmd_argument	<=	X"00000000";	-- for 4 bit data, 0x00000002
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when command25		=>	cmd_command		<=	to_std_logic_vector(24,CMD_LENGTH);  -- change to 25 for block, single for test
									cmd_argument	<=	address;
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when delay 			=>	delay_incr		<= '1';
		------------------------------------------------------------------------------
		when resp_wait		=> resp_await		<= '1';
		------------------------------------------------------------------------------
		when data_start 	=>	data_strobe		<= '1';
									data_inout		<= '1';
		------------------------------------------------------------------------------
		when data_wait		=> low_wait			<= '1';
		------------------------------------------------------------------------------
		when command13 	=> cmd_command		<= to_std_logic_vector(13,CMD_LENGTH);
									cmd_argument	<= rca & X"0000";
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when command12 	=> cmd_command		<= to_std_logic_vector(12,CMD_LENGTH);
									cmd_argument	<= X"00000000";
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when disc 			=> cmd_command		<= to_std_logic_vector(15,CMD_LENGTH);
									cmd_argument	<= rca & X"0000";
									strobe_out		<= '1';
									cmd_inout		<= '1';
		------------------------------------------------------------------------------
		when wait_read		=>	data_read		<=	'1';
									resp_await		<= '1';
		------------------------------------------------------------------------------
		when others			=>
		------------------------------------------------------------------------------
	end case;
end process SM_CMB;

--------------------------------------------------------------------------------------------
-- outward facing state flags

Sent_Cmd	<= cmd_done;
Rec_Resp <= resp_stop;

--------------------------------------------------------------------------------------------
-- find the RCA of the card
GET_RCA : process(clk_sd) is 
begin

	if rising_edge(clk_sd) then	
		if resp_stop = '1' and prev_state = command3 then
			rca <= resp_prev(39 downto 24);
		end if;	
	end if;

end process GET_RCA;

--------------------------------------------------------------------------------------------
-- delay state counter process
DELAY_CNT : process(clk_sd) is
begin

	if rising_edge(clk_sd) then
		if delay_incr = '1' then
			if delay_count = 127 then
				delay_complete <= '1';
			else
				delay_count <= delay_count + 1;
				delay_complete <= '0';
			end if;
		else
			delay_count <= 0;
			delay_complete <= '0';
		end if;
	end if;
	
end process DELAY_CNT;
			

--------------------------------------------------------------------------------------------
-- command strobe process

CMD_STB : process(clk_sd) is
begin

	if rising_edge(clk_sd) then
		if (host_state /= resp_wait) and (host_state /= delay) and (strobe_out = '1') then
		--if (host_state /= prev_state) and (host_state /= resp_wait) and (host_state /= delay) and (strobe_out = '1') then
		--if (host_state /= prev_state) and (strobe_out = '1') then
			cmd_strobe <= '1';
			cmd_await  <= '1';
		else
			cmd_strobe <= '0';
		end if;
	end if;

end process CMD_STB;

--------------------------------------------------------------------------------------------
-- port direction

-- command pin direction
Sd_Cmd 	<= cmd_output when cmd_inout = '1' else 'Z' ;

-- data pin direction
Sd_Data	<= data_output when data_inout = '1' else 'Z';

--------------------------------------------------------------------------------------------
-- find response

RESPONSE : process(clk_sd) is
	variable dly_count	:	integer range 0 to 88;
begin

	if rising_edge(clk_sd) then
	
		-- find the start of response if waiting for it
		if Sd_Cmd = '0' and resp_await = '1' then
			resp_start <= '1';
			--resp_await <= '0';
			
		end if;
		
		resp_stop  <= '0';
		
		-- response has been found
		if resp_start = '1' then
		
			if resp_count = 47 then
				
				if dly_count = 88 then
					dly_count := 0;
				
					-- flag end of response
					resp_count <= 0;
					resp_start <= '0';
					resp_stop  <= '1';
				
					-- register shifted response
					resp_prev <= resp_content;
				else
					dly_count := dly_count + 1;
				end if;
				
			else
		
				-- shift register of response
				resp_content(47 downto 1) <= resp_content(46 downto 0);
				resp_content(0) <= Sd_Cmd;
				resp_count <= resp_count + 1;
					
			end if;
			
		end if;
	
	end if;

end process RESPONSE;

Resp_Found <= resp_start;
Cmd_Prev_Rx <= resp_prev;

DAT_READ : process(Clk) is
	variable read_en	:	std_logic;
	variable	count		:	integer range 0 to 255 := 0;
begin

	if rising_edge(Clk) then
		if data_read = '1' then
			if Sd_Data = '0' then
				read_en := '1';			
			end if;
		else
			data_finish <= '0';
		end if;
		
		if read_en = '1' then
			if count = 255 then
				count := 0;
				data_finish <= '1';
				read_en := '0';
			else
				count := count + 1;
				data_r <= data_r(254 downto 0) & Sd_Data;
				data_finish <= '0';
			end if;
		end if;
	end if;

end process DAT_READ;

Data_Ready <= data_finish;
Data_Read_Out <= data_r;

--------------------------------------------------------------------------------------------
-- find data line being held low

DAT_REC : process(Clk) is
begin

	if rising_edge(Clk) then
		
		if low_wait = '1' then
			if Sd_Data = '0' then
				data_found <= '1';
			else
				data_found <= '0';
			end if;
		else
			data_found <= '0';
		end if;	
	end if;

end process DAT_REC;

Data_Rec <= data_found;
Data_Sent <= data_done;

--------------------------------------------------------------------------------------------
-- clock divider for low speed initialization

CLK_DIV : process(Clk) is
	variable count : integer range 0 to 127;
begin

	if rising_edge(Clk) then
	
		-- divide clock by 128 (50MHz / 128 ~ 390KHz)
		if count = 127 then
			count := 0;
			clk_slow <= not(clk_slow);
		else
			count := count + 1;
		end if;
	
	end if;

end process CLK_DIV;

-- choose correct clock
clk_sd <= clk_slow when clk_slow_en = '1' else Clk ;
Sd_Clk <= clk_sd;

MARK_REC : process(clk_sd) is
begin

	if rising_edge(clk_sd) then
		if State_Prog = '1' then
			Received <= '1';
		else
			Received <= '0';
		end if;
	end if;
	
end process MARK_REC;

--------------------------------------------------------------------------------------------
-- store previous command sent

PREV_TX : process(clk_sd) is
begin

	if rising_edge(clk_sd) then
	
		if strobe_out = '1' then
		
			--Cmd_Prev_Tx	<=	cmd_command;
			
		end if;	
	end if;

end process PREV_TX;

--New_State <= resp_stop;-- or cmd_done;
New_State <= cmd_done;

--------------------------------------------------------------------------------------------
-- component instantiation

-- command transmitter
CMD_OUT : cmd_tx 
	generic map
	(
		CMD_LENGTH			=> 	CMD_LENGTH,
		ARGUMENT_LENGTH	=> 	ARGUMENT_LENGTH,
		CRC_LENGTH			=> 	CMD_CRC_LENGTH
	)
   port map
	( 
		Clk 					=>		clk_sd,
      Strobe 				=>		cmd_strobe,
      Cmd 					=>		cmd_command,
      Argument 			=> 	cmd_argument,
      Output 				=> 	cmd_output,
      Done 					=>		cmd_done
	);
	
-- data transmitter
DATA_OUT : data_tx
	generic map
	(
		DATA_LENGTH			=>		DATA_LENGTH,
		INPUT_WIDTH			=>		DATA_INPUT_WIDTH,
		CRC_LENGTH			=>		DATA_CRC_LENGTH
	)
   port map
	( 
		Clk 					=>		clk_sd,
      Strobe 				=>		data_strobe,
      Data 					=> 	data_data,
      Output 				=>		data_output,
      Done 					=>		data_done,
		Data_Req				=>		data_req
	);

end behavioral;

